﻿#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

using Weazel.Gezel.Model;

#endregion

namespace Weazel.Gezel.Simulator.Reference.Setup
{
    static class ExpressionSetup
    {
        internal static void setupExpression(Simulation sim, Expression expr, Datapath rtdp, Weazel.Gezel.Model.Datapath datapath)
        {
            setupExpression(sim, expr, Symid.NOSYMBOL, rtdp, datapath);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sim"></param>
        /// <param name="expr"></param>
        /// <param name="sfgid">id of containing sfg</param>
        /// <param name="rtdp"></param>
        /// <param name="datapath"></param>
        internal static void setupExpression(Simulation simulation, Expression expression, Symid sfgid, Datapath rtdp, Weazel.Gezel.Model.Datapath datapath)
        {
            if (expression is BinaryExpression)
            {
                setupBinaryExpression(simulation, expression as BinaryExpression, sfgid, rtdp, datapath);
            }
            else if (expression.ExpressionType == ExpressionType.Constant)
            {
                simulation.signals[expression.Id] = new ConstantSignal(expression.Id, (expression as LeafExpression).Value);
            }
            else if (expression.ExpressionType == ExpressionType.Identifier)
            {
                return;
            }
            else if (expression.ExpressionType == ExpressionType.FinishDirective)
            {
                DirectiveExpression dExpr = expression as DirectiveExpression;
                FinishDirective finishDirective = new FinishDirective(simulation, dExpr.Id, sfgid, rtdp);
                simulation.directives[dExpr.Id] = finishDirective;
            }
            else if (expression.ExpressionType == ExpressionType.DisplayDirective)
            {
                setupDisplayDirective(simulation, expression, ref sfgid, rtdp, datapath);
            }
            else if (expression.ExpressionType == ExpressionType.Index)
            {
                IndexExpression iExpr = expression as IndexExpression;

                // setup index expression if needed
                setupExpression(simulation, iExpr.IndexExpr, sfgid, rtdp, datapath);

                int indexExprId = iExpr.IndexExpr.ExpressionType == ExpressionType.Identifier ?
                  SimulationSetup.getRefId(datapath, iExpr.IndexExpr as LeafExpression) : iExpr.IndexExpr.Id;
                int identifierId = SimulationSetup.getRefId(datapath, iExpr.Identifier as LeafExpression);

                simulation.signals[expression.Id] = new LookupSignal(expression.Id, simulation.signals[indexExprId], simulation.lookupTables[identifierId], simulation);
            }
            else if (expression.ExpressionType == ExpressionType.Ternary)
            {
                setupTernaryExpression(simulation, expression, ref sfgid, rtdp, datapath);
            }
            else if (expression.ExpressionType == ExpressionType.UnarySub)
            {
                UnaryExpression uExpr = expression as UnaryExpression;

                setupExpression(simulation, uExpr.Child, sfgid, rtdp, datapath);

                int childId = uExpr.Child.ExpressionType == ExpressionType.Identifier ?
                  SimulationSetup.getRefId(datapath, uExpr.Child as LeafExpression) : uExpr.Child.Id;

                simulation.signals[expression.Id] = new NegSignal(expression.Id, simulation.signals[childId], simulation);
            }
            else if (expression.ExpressionType == ExpressionType.Not)
            {
                UnaryExpression uExpr = expression as UnaryExpression;

                setupExpression(simulation, uExpr.Child, sfgid, rtdp, datapath);
                int childId = uExpr.Child.ExpressionType == ExpressionType.Identifier ?
                  SimulationSetup.getRefId(datapath, uExpr.Child as LeafExpression) : uExpr.Child.Id;

                simulation.signals[expression.Id] = new NotSignal(expression.Id, simulation.signals[childId], simulation);
            }
            else if (expression.ExpressionType == ExpressionType.Cast)
            {
                CastExpression uExpr = expression as CastExpression;

                setupExpression(simulation, uExpr.Child, sfgid, rtdp, datapath);
                int childId = uExpr.Child.ExpressionType == ExpressionType.Identifier ?
                  SimulationSetup.getRefId(datapath, uExpr.Child as LeafExpression) : uExpr.Child.Id;

                simulation.signals[expression.Id] = new CastSignal(expression.Id, uExpr.Width, uExpr.Signed ? 1 : 0, simulation.signals[childId], simulation);
            }
            else if (expression.ExpressionType == ExpressionType.Range)
            {
                RangeExpression rExpr = expression as RangeExpression;

                setupExpression(simulation, rExpr.Child, sfgid, rtdp, datapath);
                int childId = rExpr.Child.ExpressionType == ExpressionType.Identifier ?
                  SimulationSetup.getRefId(datapath, rExpr.Child as LeafExpression) : rExpr.Child.Id;

                simulation.signals[expression.Id] = new ProxySignal(expression.Id, simulation.signals[childId], (int)rExpr.From, (int)rExpr.To);
            }
            else
            {
                throw new NotImplementedException();
            }
        }

        private static void setupTernaryExpression(Simulation sim, Expression expr, ref Symid sfgid, Datapath rtdp, Weazel.Gezel.Model.Datapath datapath)
        {
            TernaryExpression tExpr = expr as TernaryExpression;
            setupExpression(sim, tExpr.Condition, sfgid, rtdp, datapath);
            setupExpression(sim, tExpr.LeftChild, sfgid, rtdp, datapath);
            setupExpression(sim, tExpr.RightChild, sfgid, rtdp, datapath);

            int conditionId = tExpr.Condition.ExpressionType == ExpressionType.Identifier ?
              SimulationSetup.getRefId(datapath, tExpr.Condition as LeafExpression) : tExpr.Condition.Id;
            int leftId = tExpr.LeftChild.ExpressionType == ExpressionType.Identifier ?
              SimulationSetup.getRefId(datapath, tExpr.LeftChild as LeafExpression) : tExpr.LeftChild.Id;
            int rightId = tExpr.RightChild.ExpressionType == ExpressionType.Identifier ?
              SimulationSetup.getRefId(datapath, tExpr.RightChild as LeafExpression) : tExpr.RightChild.Id;

            TernarySignal tSignal = new TernarySignal(
              tExpr.Id,
              sim.signals[conditionId],
              sim.signals[leftId],
              sim.signals[rightId],
              sim
              );

            sim.signals[tExpr.Id] = tSignal;
        }

        private static void setupDisplayDirective(Simulation sim, Expression expr, ref Symid sfgid, Datapath rtdp, Weazel.Gezel.Model.Datapath datapath)
        {
            DirectiveExpression dExpr = expr as DirectiveExpression;

            DisplayDirective disp = new DisplayDirective(expr.Id, sfgid, rtdp);
            sim.directives[dExpr.Id] = disp;

            foreach (Expression subExpr in dExpr.Arguments)
            {
                if (subExpr.ExpressionType == ExpressionType.Str)
                    disp.AddString((subExpr as LeafExpression).Value);
                else if (subExpr.ExpressionType == ExpressionType.Identifier)
                {
                    object identifier = datapath.GetIdentifier((subExpr as LeafExpression).Value);
                    int id;
                    if (identifier is CommonEntity)
                    {
                        id = (identifier as CommonEntity).Id;
                    }
                    else
                    {
                        throw new ApplicationException("internal error, unsupported identifier type");
                    }
                    disp.AddVariable(sim.signals[id]);
                }
                else if (subExpr.ExpressionType == ExpressionType.CycleDirective)
                {
                    disp.AddCycle();
                }
                else if (subExpr.ExpressionType == ExpressionType.ToggleDirective)
                {
                    disp.AddToggle(sim.profiler);
                }
                else if (subExpr.ExpressionType == ExpressionType.SfgDirective)
                {
                    string sfgName = sim.symbolNames[sfgid];
                    disp.AddString(sfgName);
                }
                else if (subExpr.ExpressionType == ExpressionType.DatapathDirective)
                {
                    disp.AddString(datapath.Name);
                }
                else if (subExpr.ExpressionType == ExpressionType.HexDirective)
                {
                    disp.Base = 16;
                }
                else if (subExpr.ExpressionType == ExpressionType.BinDirective)
                {
                    disp.Base = 2;
                }
                else if (subExpr.ExpressionType == ExpressionType.DecDirective)
                {
                    disp.Base = 10;
                }
                else
                {
                    setupExpression(sim, subExpr, sfgid, rtdp, datapath);
                    disp.AddVariable(sim.signals[subExpr.Id]);
                }
            }
        }

        private static void setupBinaryExpression(Simulation sim, BinaryExpression bExpr, Symid sfgid, Datapath rtdp, Weazel.Gezel.Model.Datapath datapath)
        {
            setupExpression(sim, bExpr.LeftChild, sfgid, rtdp, datapath);
            setupExpression(sim, bExpr.RightChild, sfgid, rtdp, datapath);

            int leftId = bExpr.LeftChild.ExpressionType == ExpressionType.Identifier ?
                SimulationSetup.getRefId(datapath, bExpr.LeftChild as LeafExpression) : bExpr.LeftChild.Id;
            int rightId = bExpr.RightChild.ExpressionType == ExpressionType.Identifier ?
              SimulationSetup.getRefId(datapath, bExpr.RightChild as LeafExpression) : bExpr.RightChild.Id;

            if (bExpr.ExpressionType == ExpressionType.Add)
            {
                sim.signals[bExpr.Id] = new AddSignal(bExpr.Id, sim.signals[leftId], sim.signals[rightId], sim);
            }
            else if (bExpr.ExpressionType == ExpressionType.Sub)
            {
                sim.signals[bExpr.Id] = new SubSignal(bExpr.Id, sim.signals[leftId], sim.signals[rightId], sim);
            }
            else if (bExpr.ExpressionType == ExpressionType.Mul)
            {
                sim.signals[bExpr.Id] = new MulSignal(bExpr.Id, sim.signals[leftId], sim.signals[rightId], sim);
            }
            else if (bExpr.ExpressionType == ExpressionType.Mod)
            {
                sim.signals[bExpr.Id] = new ModSignal(bExpr.Id, sim.signals[leftId], sim.signals[rightId], sim);
            }
            else if (bExpr.ExpressionType == ExpressionType.Div)
            {
                sim.signals[bExpr.Id] = new DivSignal(bExpr.Id, sim.signals[leftId], sim.signals[rightId], sim);
            }
            else if (bExpr.ExpressionType == ExpressionType.And)
            {
                sim.signals[bExpr.Id] = new AndSignal(bExpr.Id, sim.signals[leftId], sim.signals[rightId], sim);
            }
            else if (bExpr.ExpressionType == ExpressionType.Ior)
            {
                sim.signals[bExpr.Id] = new IorSignal(bExpr.Id, sim.signals[leftId], sim.signals[rightId], sim);
            }
            else if (bExpr.ExpressionType == ExpressionType.Xor)
            {
                sim.signals[bExpr.Id] = new XorSignal(bExpr.Id, sim.signals[leftId], sim.signals[rightId], sim);
            }
            else if (bExpr.ExpressionType == ExpressionType.ShiftLeft)
            {
                sim.signals[bExpr.Id] = new ShlSignal(bExpr.Id, sim.signals[leftId], sim.signals[rightId], sim);
            }
            else if (bExpr.ExpressionType == ExpressionType.ShiftRight)
            {
                sim.signals[bExpr.Id] = new ShrSignal(bExpr.Id, sim.signals[leftId], sim.signals[rightId], sim);
            }
            else if (bExpr.ExpressionType == ExpressionType.Eq)
            {
                sim.signals[bExpr.Id] = new EqSignal(bExpr.Id, sim.signals[leftId], sim.signals[rightId], sim);
            }
            else if (bExpr.ExpressionType == ExpressionType.Neq)
            {
                sim.signals[bExpr.Id] = new NeqSignal(bExpr.Id, sim.signals[leftId], sim.signals[rightId], sim);
            }
            else if (bExpr.ExpressionType == ExpressionType.Lt)
            {
                sim.signals[bExpr.Id] = new LtSignal(bExpr.Id, sim.signals[leftId], sim.signals[rightId], sim);
            }
            else if (bExpr.ExpressionType == ExpressionType.Gt)
            {
                sim.signals[bExpr.Id] = new GtSignal(bExpr.Id, sim.signals[leftId], sim.signals[rightId], sim);
            }
            else if (bExpr.ExpressionType == ExpressionType.Lte)
            {
                sim.signals[bExpr.Id] = new LteSignal(bExpr.Id, sim.signals[leftId], sim.signals[rightId], sim);
            }
            else if (bExpr.ExpressionType == ExpressionType.Gte)
            {
                sim.signals[bExpr.Id] = new GteSignal(bExpr.Id, sim.signals[leftId], sim.signals[rightId], sim);
            }
            else if (bExpr.ExpressionType == ExpressionType.Concatenation)
            {
                sim.signals[bExpr.Id] = new ConcatenationSignal(bExpr.Id, sim.signals[leftId], sim.signals[rightId], sim);
            }
            else if (bExpr.ExpressionType == ExpressionType.Assignment)
            {
                if (!sim.signals.ContainsKey(leftId))
                    throw new Exception();

                if (!sim.signals.ContainsKey(rightId))
                    throw new Exception();

                AssignSignal i = new AssignSignal(
                    bExpr.Id,
                    sfgid,
                    sim.signals[leftId],
                    sim.signals[rightId],
                    sim
                );
            }
            else
            {
                throw new NotImplementedException();
            }
        }
    }
}
